package com.nulldev.util.networking.npacket4.extensions.security;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ByteUtil;
import com.nulldev.util.data.Variables;
import com.nulldev.util.security.encryptAPI.Encryptors;
import com.nulldev.util.security.encryptAPI.Encryptors.SystemEncryptors;
import com.nulldev.util.security.encryptAPI.impls.nacl_thirdparty.NaCl;

public class PacketSecurer {

	public static enum PacketCipher {
		NACL_SHARED,
		NACL_PKPK,
		AES_128,
		AES_256,
		ICE,
		IDEA,
		XXTEA,
		DES
	}

	private final String cipher;
	private final Object o;

	/*
	 * TODO: - Add more ciphers - Allow for non-standard ciphers via the encryption
	 * API.
	 */

	public PacketSecurer(final String cipher) {
		Variables.notNullE(cipher);
		this.cipher = cipher;
		if (this.cipher.equalsIgnoreCase("NaCl-Shared")) {
			this.o = new NaCl.SecretBox(ByteUtil.randomBytes(4096));
		} else if (this.cipher.equalsIgnoreCase("NaCl")) {
			throw new UnsupportedOperationException("NaCl requires a public and private key pair!");
		} else if (this.cipher.equalsIgnoreCase("AES128")) {
			this.o = ByteUtil.randomBytes(32);
		} else if (this.cipher.equalsIgnoreCase("ICE")) {
			this.o = ByteUtil.randomBytes(32);
		} else if (this.cipher.equalsIgnoreCase("XXTEA")) {
			this.o = ByteUtil.randomBytes(32);
		} else {
			throw new UnsupportedOperationException("Unsupported cipher: " + cipher);
		}
	}

	public PacketSecurer(final String cipher, final byte[] key) {
		Variables.notNullE(cipher);
		this.cipher = cipher;
		if (this.cipher.equalsIgnoreCase("NaCl-Shared")) {
			this.o = new NaCl.SecretBox(key);
		} else if (this.cipher.equalsIgnoreCase("NaCl")) {
			throw new UnsupportedOperationException("NaCl requires a public and private key pair!");
		} else if (this.cipher.equalsIgnoreCase("AES128")) {
			this.o = key;
		} else if (this.cipher.equalsIgnoreCase("ICE")) {
			this.o = key;
		} else if (this.cipher.equalsIgnoreCase("XXTEA")) {
			this.o = key;
		} else if (this.cipher.equalsIgnoreCase("RABBIT")) {
			this.o = key;
		} else {
			throw new UnsupportedOperationException("Unsupported cipher: " + cipher);
		}
	}

	public PacketSecurer(final String cipher, final byte[] key, final byte[] otherKey) {
		Variables.notNullE(cipher);
		this.cipher = cipher;
		if (this.cipher.equalsIgnoreCase("NaCl-Shared")) {
			this.o = new NaCl.SecretBox(key);
		} else if (this.cipher.equalsIgnoreCase("NaCl")) {
			this.o = new NaCl.Box(key, otherKey);
		} else if (this.cipher.equalsIgnoreCase("AES128")) {
			this.o = key;
		} else if (this.cipher.equalsIgnoreCase("ICE")) {
			this.o = key;
		} else if (this.cipher.equalsIgnoreCase("XXTEA")) {
			this.o = key;
		} else {
			throw new UnsupportedOperationException("Unsupported cipher: " + cipher);
		}
	}

	public byte[] encrypt(final byte[] data) {
		if (this.cipher.equalsIgnoreCase("NaCl-Shared")) {
			return ((NaCl.SecretBox) this.o).box(data);
		} else if (this.cipher.equalsIgnoreCase("NaCl")) {
			return ((NaCl.Box) this.o).box(data);
		} else if (this.cipher.equalsIgnoreCase("AES128")) {
			return Encryptors.encryptorFor(SystemEncryptors.AES128, (byte[]) this.o).encryptToBytes(data);
		} else if (this.cipher.equalsIgnoreCase("ICE")) {
			return Encryptors.encryptorFor(SystemEncryptors.ICE, (byte[]) this.o).encryptToBytes(data);
		} else if (this.cipher.equalsIgnoreCase("XXTEA")) {
			return Encryptors.encryptorFor(SystemEncryptors.XXTEA, (byte[]) this.o).encryptToBytes(data);
		} else if (this.cipher.equalsIgnoreCase("RABBIT")) {
			return Encryptors.encryptorFor(SystemEncryptors.RABBIT, (byte[]) this.o).encryptToBytes(data);
		} else {
			throw new UnsupportedOperationException("Unsupported operation on cipher: " + cipher);
		}
	}

	public byte[] decrypt(final byte[] data) {
		if (this.cipher.equalsIgnoreCase("NaCl-Shared")) {
			return ((NaCl.SecretBox) this.o).open(data);
		} else if (this.cipher.equalsIgnoreCase("NaCl")) {
			return ((NaCl.Box) this.o).open(data);
		} else if (this.cipher.equalsIgnoreCase("AES128")) {
			return Encryptors.encryptorFor(SystemEncryptors.AES128, (byte[]) this.o).decryptToBytes(data);
		} else if (this.cipher.equalsIgnoreCase("ICE")) {
			return Encryptors.encryptorFor(SystemEncryptors.ICE, (byte[]) this.o).decryptToBytes(data);
		} else if (this.cipher.equalsIgnoreCase("XXTEA")) {
			return Encryptors.encryptorFor(SystemEncryptors.XXTEA, (byte[]) this.o).decryptToBytes(data);
		} else if (this.cipher.equalsIgnoreCase("RABBIT")) {
			return Encryptors.encryptorFor(SystemEncryptors.RABBIT, (byte[]) this.o).decryptToBytes(data);
		} else {
			throw new UnsupportedOperationException("Unsupported operation on cipher: " + cipher);
		}
	}

	public static void main(String[] args) throws Exception {
		final PacketSecurer sec = new PacketSecurer("XXTEA");
		final String p = "Hello world :)";
		JVM.println("payload: " + p);
		final byte[] enc = sec.encrypt(p.getBytes());
		JVM.println("encrypted: " + new String(enc));
		final byte[] dec = sec.decrypt(enc);
		JVM.println("decrypted: " + new String(dec));
	}

	public byte[] getKey() {
		if (this.cipher.equalsIgnoreCase("NaCl-Shared")) {
			return ((NaCl.SecretBox) this.o).get_key();
		} else if (this.cipher.equalsIgnoreCase("NaCl")) {
			return ((NaCl.Box) this.o).public_key();
		} else if (this.cipher.equalsIgnoreCase("AES128")) {
			return (byte[]) this.o;
		} else if (this.cipher.equalsIgnoreCase("ICE")) {
			return (byte[]) this.o;
		} else if (this.cipher.equalsIgnoreCase("XXTEA")) {
			return (byte[]) this.o;
		} else if (this.cipher.equalsIgnoreCase("RABBIT")) {
			return (byte[]) this.o;
		} else {
			throw new UnsupportedOperationException("Unsupported cipher: " + cipher);
		}
	}

	public String cipher() {
		return this.cipher;
	}
}
